home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / 1989 / 05 / tawk.asc < prev    next >
Text File  |  1989-05-12  |  22KB  |  712 lines

  1. _TAWK, A Simple Interpreter in C++_
  2. by Bruce Eckel
  3.  
  4.  
  5. [LISTING ONE]
  6.  
  7.  
  8. // FIELD.HXX: used by csascii class to build a single field.
  9. // Fields are collected by csascii to create a record.
  10. // by Bruce Eckel,
  11. #include <stream.hxx>
  12.  
  13. class field { // one field in a comma-separated ASCII record
  14.   istream * input; // where to get the data
  15.   char * data;
  16.   int length, fsize;
  17.   int end_of_file;  // flag to indicate the end of file happened
  18.   void getfield();  // recursive function to read in a field;
  19.            // treats data, length & input as globals
  20.   int infield; // flag used by getfield() to determine whether
  21.         // it's inside a quoted field
  22.  public:
  23.   field(istream & instream);
  24.   ~field();
  25.   friend ostream& operator<<(ostream &s, field & f) {
  26.     s << f.data;
  27.     return s;
  28.   }
  29.   int eof() { return end_of_file; }  // to check for end
  30.   int size() { return fsize;} 
  31.   int last_length() {return length; }
  32.   char * string() { return data; }
  33. };
  34.  
  35.  
  36. [LISTING TWO]
  37.  
  38. // FIELD.CXX: definitions for class field
  39. // A "recursive descent" scanning scheme is used because field
  40. // length is always unknown.
  41. // by Bruce Eckel
  42. #include "field.hxx"
  43.  
  44. field::field(istream & instream) {
  45.   input = &instream;
  46.   length = 0;
  47.   end_of_file = 0; // set flag to say "we're not at the end"
  48.   infield = 0; // set flag to say "we're not inside a field"
  49.   data = (char *)0; // to show no memory has been allocated
  50.   getfield();  // recursively get characters until end of field
  51. }
  52.  
  53. field::~field() {
  54.   delete data;  // if no memory has been allocated,
  55.   // data = (char *)0 so this will have no effect.
  56. }        
  57.  
  58. // A Comma-separated ASCII field is contained in quotes to allow
  59. // commas within the field; these quotes must be stripped out
  60. void field::getfield() {
  61.   char c;
  62.   // This happens when DEscending:
  63.   if((input->get(c)).eof() ) {
  64.     end_of_file++;  // just say we reached the end...
  65.     return;
  66.   }
  67.   else  // watch out for the Unix vs. DOS LF/CR problem here:
  68.     if (((c != ',') || infield) && (c != '\n')) {  
  69.       if ( (c != '"') && (c != '\r')) // watch for quotes or CR
  70.         length++;  // no quotes -- count this character
  71.       else {
  72.         if ( c == '"') 
  73.           infield = !infield;  // if we weren't inside a field
  74.           // and a quote was encountered, we are now inside 
  75.           // a field.  If we were inside a field and a quote 
  76.           // was found, we're out of the field.
  77.         c = 0; // a quote or CR; mark it so it isn't included
  78.       }
  79.       getfield();  // recursively get characters in field
  80.       // after returning from function call, we jump past
  81.       // the following "else" part to finish the recursion
  82.     }
  83.     else { // This happens once, when the terminator is found:
  84.       fsize = length;  // remember how long the string is
  85.       data = new char[length + 1]; // space for null terminator
  86.       data[length] = '\0';  // highest index is "length" 
  87.         // when you allocate an array of length + 1
  88.       length--;  // notice we don't insert the delimiter
  89.       // Now the first "if" statement evaluates to TRUE and
  90.       // the function rises back up.
  91.       return;
  92.     }
  93.   // This happens when Ascending:
  94.   if ( c ) // if it wasn't a quote or CR,
  95.     data[length--] = c;  // put chars in as we rise back up...
  96. }
  97.  
  98.  
  99. [LISTING THREE]
  100.  
  101. // CSASCII.HXX: class to manipulate comma-separated ASCII
  102. // database files.
  103. //by Bruce Eckel
  104. #include <stream.hxx>
  105. #include "field.hxx"
  106.  
  107. class csascii {  // manipulates comma-separated ascii files,
  108. // generated by most database management systems (generated and
  109. // used by the BASIC programming language).  Each field
  110. // is separated by a comma; records are separated by newlines.
  111.   int fieldcount;
  112.   field ** data; // an array to hold the entire record
  113.   istream * datafile; // file with comma separated ASCII input
  114.   int readrecord(); // private function to read a record
  115.  public:
  116.   csascii( char * filename );  // Open file, get first record
  117.   ~csascii(); // destructor
  118.   int next(); // get next record, return 0 when EOF
  119.   field & operator[](int index); // select a field
  120.   int number_of_fields() { return fieldcount; }
  121. };
  122.  
  123.  
  124.  
  125. [LISTING FOUR]
  126.  
  127. // CSASCII.CXX: function definitions for comma-separated
  128. // ascii database manipulation class
  129. // by Bruce Eckel,
  130. #include "csascii.hxx"
  131.  
  132. int csascii::readrecord() {
  133.   for (int fieldnum = 0; fieldnum < fieldcount; fieldnum++ ) {
  134.     data[fieldnum] = new field(*datafile);
  135.     if (data[fieldnum]->eof()) return 0;
  136.   }
  137.   return 1;
  138. }    
  139.  
  140. csascii::csascii( char * filename ) {
  141.   char c;
  142.   fieldcount = 0;
  143.   int quote = 0;
  144.   // first, determine the number of fields in a record:
  145.   {
  146.     // See text for dangers of opening files this way:
  147.     istream infile(new filebuf->open(filename, input));
  148.     while(infile.get(c), c != '\n') {
  149.       // keep track of being inside a quoted string:
  150.       if (c == '"') quote = !quote;  
  151.       // fields are delimited by unquoted commas:
  152.       if ( c == ',' && !quote) 
  153.         fieldcount++;
  154.     }
  155.   }  // infile goes out of scope; file closed
  156.   fieldcount++; // last field terminated by newline, not comma
  157.   // an array of field pointers:
  158.   data = new field * [ fieldcount ]; 
  159.   // re-open at start; dynamically allocate so it isn't scoped:
  160.   datafile = new istream(new filebuf->open(filename, input));
  161.   readrecord();
  162. }
  163.  
  164. csascii::~csascii() {
  165.   delete data;
  166.   delete datafile; // calls istream destructor to close file
  167. }
  168.  
  169. int csascii::next() {
  170.   for (int i = 0; i < fieldcount; i++ )
  171.     delete data[i];  // free all the data storage
  172.   return readrecord(); // 0 when end of file
  173. }
  174.  
  175. field & csascii::operator[](int index) {
  176.   if (index >= fieldcount) {
  177.     cerr << "index too large for number of fields in record\n";
  178.     exit(1);
  179.   }
  180.   return *(data[index]);
  181. }
  182.  
  183.  
  184. [LISTING FIVE]
  185.  
  186. // LOOKUP.CXX: simple use of csascii to find name in a database
  187. // by Bruce Eckel,
  188. #include "csascii.hxx"
  189. #include <string.h>
  190.  
  191. main(int argc, char ** argv) {
  192.   if (argc < 2) {
  193.     cerr << "usage: lookup lastname\n";
  194.     exit(1);
  195.   }
  196.   // This puts the database file in the root directory:
  197.   csascii file("\\ppquick.asc"); // create object & open file
  198.   int found = 0;  // indicates one record was found
  199.   do {
  200.     if (strcmp(file[0].string(),argv[1]) == 0) {
  201.       found++;  // found one.  File is sorted, so if we stop
  202.       // finding them, quit instead of wasting time.
  203.       cout << chr(27) << "[2J"; // ANSI clear screen
  204.       for (int i = 0; i < file.number_of_fields(); i++) 
  205.         cout << file[i] << "\n";
  206.       cout << chr(27) << "[7m" << "press any key" << 
  207.         chr(27) << "[0m";
  208.       if( getch() == 27) break;
  209.     }  else if (found) exit(0);  // quit if that was the last
  210.   } while (file.next());
  211. }
  212.  
  213.  
  214. [LISTING SIX]
  215.  
  216.  
  217. // PARSE.HXX: class to parse a tawk script file.  Creates
  218. // a structure which can be used at run-time to "execute" 
  219. // the tawk script.
  220. // by Bruce Eckel,
  221. #include <stream.hxx>
  222.  
  223. // types of tokens the scanner can find:
  224. enum tokentype { 
  225.   fieldnumber, string, if_, else_, endif_, phase_change 
  226. };
  227.  
  228. // preamble and conclusion of the tawk script are only executed
  229. // once, while main is executed once for every data record
  230. enum phase { preamble, tmain, conclusion};
  231.  
  232. class token {
  233.   tokentype ttype;
  234.   union {  // an "anonymous union"
  235.     int fieldnum;  // if type is a fieldnumber
  236.     unsigned char * literal; // if type is a string
  237.   };
  238.   int if_level;  // if this is an if_, then_, or else_
  239.   // private functions:
  240.   void get_token();  // recursive descent scanner
  241.   // Functions to help in scanning:
  242.   void getnext(char & c); // used by get_token();
  243.   unsigned char get_value(char delimiter, char * msg); 
  244.   void dumpline(); // for @! comments
  245.   void error(char * msg = "", char * msg2 = "");
  246.  public:
  247.   token(istream & input);
  248.   ~token();
  249.   friend ostream & operator<<(ostream &s, token &t); 
  250.   int field_number() { return fieldnum; }
  251.   int token_type() { return ttype; }
  252.   int nesting_level() { return if_level;}
  253. };
  254.  
  255. // The following is called a "container class," since its sole
  256. // purpose is to hold a list of objects (tokens, in this case):
  257. class parse_array {
  258.   token ** tokenarray; // an array of token pointers
  259.   istream * parse_stream;
  260.   int token_count;
  261.   int end; // the size of the array
  262.   phase p_section; // of the program (preamble, etc.)
  263.   void build_array(); // another recursive function
  264.  public:
  265.   parse_array(istream & input);
  266.   ~parse_array();
  267.   int size() { return end; } // how big is it?
  268.   token & operator[](int index); // select a token
  269.   phase section() { return p_section; }
  270. };
  271.  
  272.  
  273. [LISTING SEVEN]
  274.  
  275. // PARSE.CXX: class parse function definitions
  276. // by Bruce Eckel,
  277. #include "csascii.hxx"  
  278. #include "parse.hxx"
  279. #include <ctype.h> 
  280. #include <stdlib.h>
  281.  
  282. // The following are "file static," which means no one outside 
  283. // this file can know about them.  This is the meaning when a 
  284. // global variable is declared "static."
  285. static istream * tokenstream;
  286. static int length; // to remember size of string
  287. static int line_number = 1;  // line counting for errors
  288. static int if_counter = 0; // monitors "if" statement nesting
  289. static phase program_section = preamble;  // ... until @main
  290. static int end_of_file = 0; // zero means not end of file 
  291.  
  292. token::token(istream & input) {
  293.   // initialize values and start the descent
  294.   tokenstream = &input;
  295.   length = 0;
  296.   get_token();  // recursively get characters to end of token
  297. }
  298.  
  299. token::~token() { // delete heap if any has been allocated:
  300.   if (ttype == string)
  301.     delete literal;
  302. }
  303.  
  304. void token::error(char * msg, char * msg2) {
  305.   cerr << "token error on line " << line_number << ": " <<
  306.       msg << " " << msg2 << "\n";
  307.   exit(1);
  308. }
  309.  
  310. ostream & operator<<(ostream &s, token &t) {
  311.   switch (t.ttype) {
  312.     case string: 
  313.       s << (char *)t.literal;
  314.       break;
  315.     case fieldnumber: // only for testing
  316.       s << " fieldnumber: " << t.fieldnum << "\n";
  317.   }
  318.   return s;
  319. }
  320.  
  321. // Get a character from the tokenstream, checking for
  322. // end-of-file and newlines
  323. void token::getnext(char & c) {
  324.   if(end_of_file)
  325.     error("attempt to read after @end statement\n",
  326.        "missing @conclusion ?");
  327.   if((tokenstream->get(c)).eof() ) 
  328.     error("@end statement missing");
  329.   if (c == '\n') 
  330.     line_number++; // keep track of the line count
  331. }
  332.  
  333. // See text for description of tokens
  334. void token::get_token() {
  335.   char c;
  336.   // This happens when DEscending:
  337.   getnext(c);
  338.   if ( c == '@') {
  339.     if (length == 0) { // length 0 means start of token
  340.       getnext(c);
  341.       switch(c) {
  342.         case '!': // comment line
  343.           dumpline(); // dump the comment
  344.           get_token(); // get a real token
  345.           break;
  346.         case 'p' : case 'P' : // preamble statement
  347.           if ( program_section != preamble )
  348.             error("only one preamble allowed");
  349.           dumpline(); // just for looks, ignore it
  350.           get_token(); // get a real token
  351.           break;
  352.         case 'm' : case 'M' : // start of main loop
  353.           dumpline(); // toss rest of line
  354.           program_section = tmain;
  355.           ttype = phase_change;
  356.           return; // very simple token
  357.         case 'c' : case 'C' : // start conclusion
  358.           dumpline(); 
  359.           program_section = conclusion;
  360.           ttype = phase_change;
  361.           return; // very simple token
  362.         case 'e' : case 'E': // end statement
  363.           end_of_file++;  // set flag
  364.           ttype = fieldnumber; // so destructor doesn't
  365.                     // delete free store for this token.
  366.           if (if_counter)
  367.             error("unclosed 'if' statement(s)");
  368.           return;
  369.         case '(' :
  370.           if ( program_section == preamble ||
  371.             program_section == conclusion )
  372.            error("@() not allowed in preamble or conclusion");
  373.           fieldnum = get_value(')',"@()");
  374.           ttype = fieldnumber;
  375.           // This is a complete token, so quit
  376.           return;
  377.         case '<' :
  378.           c = get_value('>',"@<>");  
  379.           length++;
  380.           get_token(); // get more...
  381.           break;
  382.         case '?' : // beginning of an "if" statement
  383.           if ( program_section == preamble ||
  384.             program_section == conclusion )
  385.            error("@? not allowed in preamble or conclusion");
  386.           fieldnum = get_value('@',"@?@");
  387.           ttype = if_;
  388.           getnext(c);  // just eat the colon
  389.           if(c != ':')
  390.             error("@? must be followed by @: (then)");
  391.           if_level = ++if_counter;  // for nesting
  392.           return;
  393.         case '~' : // the "else" part of an "if" statement
  394.           ttype = else_;
  395.           if_level = if_counter;
  396.           return;
  397.         case '.' : // "endif" terminator of an "if" statement
  398.           ttype = endif_;
  399.           if_level = if_counter--;
  400.           if(if_counter < 0)
  401.             error("incorrect nesting of if-then-else clauses");
  402.           return;
  403.         case '@' : // two '@' in a row mean print an '@'
  404.           length++;  // just leave '@' as the value of c
  405.           get_token();
  406.           break;
  407.         default:
  408.           error("'@' must be followed by:",
  409.           "'(', '<', '?',':','~','.','p','m','c' or '@'");
  410.       }
  411.     } else { // an '@' in the middle of a string; terminate 
  412.       // the string.  Putback() is part of the stream class. 
  413.       // It is only safe to put one character back on the input 
  414.       tokenstream->putback(c); // to be used by the next token
  415.       // allocate space, put the null in and return up the stack
  416.       literal = new unsigned char[length + 1]; // space for '\0'
  417.       literal[length--] = '\0'; // string delimiter
  418.       ttype = string; // what kind of token this is
  419.       return; // back up the stack
  420.     }
  421.   } else { // not an '@', must be plain text
  422.     length++;
  423.     get_token();
  424.   }
  425.   // This occurs on the "tail" of the recursion:
  426.   literal[length--] = c;  // put chars in as we rise back up...
  427. }
  428.  
  429. // This function is used by get_token when it encounters a @(
  430. // or a @< to get a number until it finds "delimiter."
  431. // If an error occurs, msg is used to notify the user what
  432. // kind of statement it is.
  433. unsigned char token::get_value(char delimiter, char * msg) { 
  434.   char c;
  435.   char buf[5];
  436.   int i = 0;
  437.   while(getnext(c), c != delimiter) { 
  438.     if (!isdigit(c))    
  439.       error("must use only digits inside", msg);
  440.     buf[i++] = c;
  441.   }
  442.   buf[i] = 0;
  443.   return atoi(buf);
  444. }
  445.  
  446. void token::dumpline() { // called when '@!' encountered
  447.   char c;
  448.   while(getnext(c), c != '\n')
  449.     ; // just eat characters until newline
  450. }        
  451.  
  452. // Since there's no way to know how big a parse_array is
  453. // going to be until the entire tawkfile has been tokenized,
  454. // the recursive approach is again used:
  455.  
  456. parse_array::parse_array(istream & input) {
  457.   parse_stream = &input;
  458.   token_count = 0;
  459.   p_section = program_section; // so we know at run-time
  460.   build_array();
  461. }
  462.  
  463. void parse_array::build_array() {
  464.   token * tk = new token(*parse_stream);
  465.   if( ! end_of_file && tk->token_type() != phase_change) {
  466.     // normal token, not end of file or phase change:
  467.     token_count++;
  468.     // recursively get tokens until eof or phase change:
  469.     build_array(); 
  470.   } else { // end of file or phase change
  471.     // only done once per object: 
  472.     // allocate memory and return up the stack
  473.     tokenarray = new token * [end = token_count];
  474.     if(token_count) token_count--; // only if non-zero
  475.     return;
  476.   }
  477.   tokenarray[token_count--] = tk;  // performed on the "tail"
  478. }
  479.  
  480.  
  481. parse_array::~parse_array() {
  482.   for (int i = 0; i < end; i++)
  483.     delete tokenarray[i];
  484.   delete tokenarray;
  485. }
  486.  
  487. token & parse_array::operator[](int index) {
  488.   if ( index >= end ) {
  489.     cerr << "parse_array error: index " << index 
  490.       << " out of bounds\n";
  491.     exit(1);
  492.   }
  493.   return *tokenarray[index];
  494. }
  495.  
  496.  
  497. [LISTING EIGHT]
  498.  
  499. // TAWK.CXX: parses a tawk script and reads an ascii file; 
  500. // generates results according to the tawk script.
  501. // by Bruce Eckel,
  502. #include "csascii.hxx"  
  503. #include "parse.hxx"
  504.  
  505. main (int argc, char * argv[]) {
  506.   int screen = 0;  // flag set true if screen output desired
  507.   if (argc < 3) {
  508.     cerr << "usage: tawk tawkfile datafile\n" <<
  509.         "trailing -s pages output to screen";
  510.     exit(1);
  511.   }
  512.   if (argc == 4) {
  513.     if (argv[3][0] != '-') {
  514.       cerr << "must use '-' before trailing flag\n";
  515.       exit(1);
  516.     } else
  517.     if (argv[3][1] != 's') {
  518.       cerr << "'s' is only trailing flag allowed";
  519.       exit(1);
  520.     } else
  521.       screen++; // set screen output flag true
  522.   }
  523.   istream tawkfile(new filebuf->open(argv[1], input));
  524.   parse_array Apreamble(tawkfile);  // the @preamble
  525.   parse_array Amain(tawkfile);  // the @main section
  526.   parse_array Aconclusion(tawkfile); // the @conclusion
  527.   csascii datafile(argv[2]); // make a comma-separated ASCII
  528.                              // object from the second arg
  529.   // ------ @preamble  ------ 
  530.   for (int i = 0; i < Apreamble.size(); i++)
  531.     cout << Apreamble[i]; // preamble can only contain strings
  532.   if(screen) {
  533.     // ANSI reverse video sequence:
  534.     cout << chr(27) << "[7m" << "press any key" << 
  535.       chr(27) << "[0m";
  536.     getch();
  537.   }
  538.   // ------ The Central Loop (@main) -------
  539.   do {  // for each record in the data file
  540.     if(screen) cout << chr(27) << "[2J"; // ANSI clear screen
  541.     for(int i = 0; i < Amain.size(); i++) {
  542.       switch(Amain[i].token_type()) {
  543.         case fieldnumber:
  544.           cout << datafile[Amain[i].field_number()];
  545.           break;
  546.         case string:
  547.           cout << Amain[i];
  548.           break;
  549.         case if_:
  550.           int fn = Amain[i].field_number();
  551.           if (datafile[fn].size() == 0) { // conditional false
  552.             int level = Amain[i].nesting_level();
  553.             // find the "else" statement on the same level:
  554.             while ( !(Amain[i].token_type() == else_ 
  555.                 && Amain[i].nesting_level() == level))
  556.                   i++;
  557.           } // conditional true -- just continue
  558.           break;
  559.         case else_: // an "if" conditional was true so skip
  560.           // all the statements in the "else" clause
  561.           int level = Amain[i].nesting_level();
  562.           // find the "endif" statement on the same level:
  563.           while ( !(Amain[i].token_type() == endif_ 
  564.               && Amain[i].nesting_level() == level))
  565.                 i++;
  566.           break;
  567.         case endif_: // after performing the "else" clause
  568.           break; // ignore it; only used to find the end
  569.           // of the conditional when "if" is true.
  570.         default: // should never happen (caught in parsing)
  571.           cerr << "unknown statement encountered at run-time\n";
  572.           exit(1);
  573.       }
  574.     }
  575.     if(screen) {
  576.       cout << chr(27) << "[7m" << 
  577.         "press a key (ESC quits)" << chr(27) << "[0m";
  578.       if( getch() == 27) break;
  579.     }
  580.   } while (datafile.next()); // matches do { ...
  581.   //  ------ @conclusion ------ 
  582.   for ( i = 0; i < Aconclusion.size(); i++)
  583.     cout << Aconclusion[i]; //conclusion contains only strings
  584. }
  585.  
  586.  
  587.  
  588. [LISTING NINE]
  589.  
  590. # makefile for tawk.exe & lookup.exe
  591. # Zortech C++:
  592. CPP = ztc
  593. # Glockenspiel C++ w/ MSC 4:
  594. #CPP = ccxx !4
  595.  
  596. all: tawk.exe lookup.exe
  597.  
  598. tawk.exe : tawk.obj parse.obj csascii.obj field.obj 
  599.         $(CPP)  tawk.obj parse.obj csascii.obj field.obj
  600.         
  601. lookup.exe : lookup.cxx csascii.obj field.obj 
  602.         $(CPP) lookup.cxx csascii.obj field.obj
  603.  
  604. tawk.obj : tawk.cxx parse.hxx csascii.hxx field.hxx
  605.         $(CPP)  -c tawk.cxx
  606.  
  607. parse.obj : parse.cxx parse.hxx
  608.         $(CPP)  -c parse.cxx
  609.  
  610. csascii.obj : csascii.cxx csascii.hxx field.hxx
  611.         $(CPP)  -c csascii.cxx
  612.  
  613. field.obj : field.cxx field.hxx
  614.         $(CPP)  -c field.cxx
  615.  
  616.  
  617.  
  618. [LISTING TEN]
  619.  
  620. @! REFORM.TWK
  621. @! A tawk script to reformat a comma-separated ASCII file
  622. @! with 6 fields.  This creates a new CS-ASCII file with
  623. @! fields 4 and 5 combined.
  624. @main
  625. "@(0)","@(1)","@(2)","@(3)","@(4)@?4@: @~@.@(5)"
  626. @conclusion
  627. @end
  628.  
  629.  
  630.  
  631. [LISTING ELEVEN]
  632.  
  633.  
  634. @! WALLET.TWK
  635. @! Tawkfile to create a tiny phone listing for a wallet
  636. @! on a Hewlett-Packard Laserjet-compatible printer
  637. @! From a comma-separated ASCII file generated by a DBMS
  638. @preamble
  639. @<27>&l5C@! approximately 10 lines per inch
  640. @<27>(s16.66H@! small typeface, built into Laserjet
  641. @main
  642. @! last, first, (area code) phone1
  643. @(0),@(1)(@(2))@?3@:@(3)
  644. @ phone2, if it exists
  645. @?4@:@(4)
  646. @~@.@~@?4@:@(4)
  647. @~
  648. @.@.@conclusion
  649. @<27>E @! Reset the Laserjet
  650. @end
  651.  
  652. [EXAMPLE 1]
  653.  
  654. class tiny {
  655.   // private stuff here (this is a comment)
  656.   int i;
  657.  public: // public stuff here:
  658.   print() { // an "in-line" function
  659.     printf("i = %d\n",i);
  660.   }
  661.   tiny(int j); // constructors have the class name
  662.   ~tiny() {} // destructors use a tilde
  663. };  // classes end with a brace and a semicolon
  664.  
  665. tiny::tiny(int j) { // non inline definition
  666.   i = j;
  667. }
  668.  
  669. main() {
  670.   tiny A(2);  // implicit constructor call
  671.   // A.i = 30; // error! private member
  672.   A.print(); // calling a member function
  673.   // implicit destructor call at end of scope
  674. }
  675.  
  676.  
  677.  
  678.  
  679.  
  680. [EXAMPLE 2]
  681.  
  682.  
  683. #include <stream.hxx> // cout automatically defined
  684. main() {
  685.   cout << "Hello, world!\n" << "I am " 
  686.        << 6 << "today!\n";
  687. }
  688.  
  689.  
  690.  
  691.  
  692. [EXAMPLE 3]
  693.  
  694. filebuf f1;
  695. if (f1.open(argv[1],input) == 0) {
  696.   cout << "cannot open " << argv[1] << "\n";
  697.   exit(1);
  698. }
  699. istream infile(&f1);
  700.  
  701.  
  702.  
  703. [EXAMPLE 4] 
  704.  
  705.  
  706. "Ball","Mike","Oregon Software C++ Compiler"
  707. "Bright","Walter","Zortech C++ Compiler"
  708. "Carolan","John","Glockenspiel C++ Translator"
  709. "Stroustrup","Bjarne","AT&T, C++ Creator"
  710. "Tiemann","Michael","Free Software Foundation C++ Compiler"
  711.  
  712.